package com.ivan.toolkit.common;

import java.util.LinkedList;

import com.google.common.collect.Lists;

import lombok.extern.log4j.Log4j2;


@Log4j2
public class Dispatcher<T> implements Runnable, Module {
		
	LinkedList<T> queue = Lists.newLinkedList();
		
	private boolean stop = false;	
	
	Thread thread = null;
	
	public Dispatcher() {
		initialize();
	}

	@Override
	public void initialize() {
		if(this.thread == null) {
			this.thread = new Thread(this, this.getClass().getSimpleName());
			this.thread.setDaemon(true);
			this.thread.start();
		}
	}

	@Override
	public void terminate() {

		// set stop flag
		this.stop = true;

		// notify the thread
		synchronized(queue) {
			queue.clear();
			try {
				queue.notify();
			} catch (Exception e) {
				log.error("Notify failed.", e);
			}
		}
		
		// wait thread exit
		try {
			thread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	
	public void reset() {
		
		// notify the thread
		synchronized(queue) {
			queue.clear();
		}
		
	}
	

	@Override
	public void run() {
		
		stop = false;
		
		while(!stop) {
			
			T value = null;
			
			synchronized(queue) {
				
				if(queue.isEmpty()) {
					
					try{
						queue.wait();
					} catch (InterruptedException e) {
						log.info("Wait data interrupted.", e);
						break;
					}
					
				} else {
					value = queue.poll();
				}
				
				// check stop
				if(stop) break;
				
				// check value
				if(value == null) continue;
			}
			
			// dispatch value
			try{
				this.dispatch(value);
			} catch(Exception e) {
				log.info("Dispatching error.", e);
			}
			
		}
	}

	public void feed(T value) {
		synchronized(queue) {
			queue.addLast(value);
			onQueueSize(queue.size());
			try {
				queue.notify();
			} catch (IllegalMonitorStateException e) {
				log.error("Notify failed.", e);
			}
		}
	}
	
	public int getQueueSize() {
		return queue.size();
	}
	
	protected void onQueueSize(int size) {
		
	}
	
	protected void dispatch(T value) {
	}

}
